package com.opencee.cloud.api.gateway.filter.factory;

import org.apache.dubbo.common.URLBuilder;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.parse;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;

/**
 * 自定义RedirectTo追加截取访问路径和参数
 * RedirectToWith=302,http://www.baidu.com  ->  http://www.baidu.com/a?param1=1&param2=2
 *
 * @author yadu
 */
public class RedirectToWithGatewayFilterFactory extends AbstractGatewayFilterFactory<RedirectToWithGatewayFilterFactory.Config> {

    public static final String STATUS_KEY = "status";
    public static final String URL_KEY = "url";

    public RedirectToWithGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(STATUS_KEY, URL_KEY);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return apply(config.status, config.url);
    }

    public GatewayFilter apply(String statusString, String urlString) {
        final HttpStatus httpStatus = parse(statusString);
        Assert.isTrue(httpStatus.is3xxRedirection(), "status must be a 3xx code, but was " + statusString);
        final URL url;
        try {
            url = URI.create(urlString).toURL();
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("Invalid url " + urlString, e);
        }
        return apply(httpStatus, url);
    }

    public GatewayFilter apply(HttpStatus httpStatus, URL url) {

        return (exchange, chain) ->
                chain.filter(exchange).then(Mono.defer(() -> {
                    if (!exchange.getResponse().isCommitted()) {
                        setResponseStatus(exchange, httpStatus);
                        ServerHttpRequest request = exchange.getRequest();
                        URL newUrl = null;
                        try {
                            newUrl = buildRedirectUrlWithParams(url, request);
                        } catch (IOException e) {
                            newUrl = url;
                        }
                        final ServerHttpResponse response = exchange.getResponse();
                        response.getHeaders().set(HttpHeaders.LOCATION, newUrl.toString());
                        return response.setComplete();
                    }
                    return Mono.empty();
                }));
    }

    protected URL buildRedirectUrlWithParams(URL url, ServerHttpRequest request) throws IOException {
        URLBuilder builder = new URLBuilder();
        builder.setHost(url.getHost());
        builder.setPort(url.getPort());
        builder.setProtocol(url.getProtocol());
        builder.setPath(url.getPath() + request.getPath());
        builder.addParametersIfAbsent(request.getQueryParams().toSingleValueMap());
        return builder.build().toJavaURL();
    }

    public static class Config {
        String status;
        String url;

        public String getStatus() {
            return status;
        }

        public void setStatus(String status) {
            this.status = status;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }

}
